package net.trustie.one;

import core.ModelPipeline;
import core.PageModelPipeline;
import extension.RawPage;
import net.trustie.dao.RecordDao;
import net.trustie.downloader.DataBasePageErrorOutPut;
import net.trustie.downloader.PageDao;
import net.trustie.model.cto51_blog_Model;
import net.trustie.utils.Constant;
import net.trustie.utils.DateHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * <p>Title:抽取线程</p>
 * <p>Description:抽取项目的任务主线程</p>
 * @author liuyizhou
 * @date 2016年12月22日
 */
@Component("extractThread")
@Scope("prototype")
public class ExtractThread implements Runnable{
    /** 待抽取的站点名称 */
    private String site;
    /** 待抽取结果条数限制 */
    private static final int BatchExtractSize = Constant.BATCH_EXTRACT_SIZE;
    /** 模板名称列表 */
    public List<String> modelName = new ArrayList<String>();
    /** 动态生成站点模板 */
    private Class pageModel;
    @Resource
    private PageDao pagedao;

    /** 输出错误页面 */
    @Qualifier("errorPageToDB")
    @Autowired
    private DataBasePageErrorOutPut pageErrorOutPut;

    /** records表操作接口实例化 */
    @Resource
    private RecordDao recordDao;

    public ExtractThread(){

    }

    public ExtractThread(String site){
        setSite(site);
    }

    public void setSite(String site){
        this.site = site;
    }

    public void run() {
        //设置thread name 以便查看输出日志
        Thread.currentThread().setName(site + "_thread");
        this.pageErrorOutPut.setTableName(site + "_error_page");
        try {

            System.out.println(Thread.currentThread().getName() + ": " +
                    "extract begain" + " @ " + DateHandler.getExtractTime());

            if(site.equals("51cto_blog"))
                pageModel = cto51_blog_Model.class;
            else
                pageModel = Class.forName("net.trustie.model." + site +"_Model");

            modelName.add(pageModel.getCanonicalName());

            //获取该站点的抽取模板和要抽取的html页面
            int lastId = getLastId(site);
            List<RawPage> pages = getPages(site,lastId);

            Extractor extractor = new Extractor();
            RawPage result = null;
            while(pages.size() > 0){
                /**
                 * 注意这里的page就是detail表中的一条记录。
                 */
                for( RawPage page : pages){
                    try{
                        long startTime=System.currentTimeMillis();   //获取开始时间
                        result = extractor.extract(page,pageModel);
                        long endTime=System.currentTimeMillis(); //获取结束时间
//                       System.out.println("页面抽取时间： "+(endTime-startTime)+"ms");

                        startTime=System.currentTimeMillis();   //获取开始时间
                        //持久化  并 更新抽取历史
                        saveResult(site,result);
                        endTime=System.currentTimeMillis(); //获取结束时间
//                        System.out.println("结果保存时间： "+(endTime-startTime)+"ms");

                    }catch (Exception e){
//                        e.printStackTrace();
//                        pageErrorOutPut.returnErrorPage(page, e);  //错误页面
                    }
                }
                //结果入库之后再更新lastId.注意，处理一批才会存一次
                updateLastId(site,lastId + pages.size());
                lastId = getLastId(site);
                pages = getPages(site,lastId);
            }
            Application.extractState.put(site, false);
            System.out.println(Thread.currentThread().getName() + ": " +
                    "extract over, id=" + lastId + " @ " + DateHandler.getExtractTime());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch(Exception e){
            System.out.println(e.getMessage());
            e.printStackTrace();
            System.out.println(Thread.currentThread().getName() + ": " +" 发生异常 ");
        }


    }
    private void saveResult(String site, RawPage rawPage ) {

        ModelPipeline pl = new ModelPipeline();
        try {
            pl.put(pageModel,(PageModelPipeline) AppContext.appContext.getBean(site + "_pipeline"));
        } catch (BeansException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        if (!rawPage.getPage().getResultItems().isSkip()) {
            pl.process(rawPage.getPage().getResultItems(), null);
        }
        if (!rawPage.getPage()
                .isAllResultSkip(
                        modelName.toArray(new String[modelName
                                .size()]))) {
            rawPage.setStored(true);
        }else{

        }

        if (!rawPage.isExtracted() || !rawPage.isStored())
            pageErrorOutPut.returnErrorPage(rawPage,
                    "May Caused By Model Problem! Or Page Error!");


    }
    private void updateLastId(String site, int currentId) {
        recordDao.updateRecord(site,currentId);
    }

    private void saveResult(String site, List<RawPage> results) {
        //System.out.println("save result");
        ModelPipeline pl = new ModelPipeline();
        try {
            pl.put(pageModel,(PageModelPipeline) AppContext.appContext.getBean(site + "_pipeline"));
        } catch (BeansException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        for(RawPage rawPage : results){
            if (!rawPage.getPage().getResultItems().isSkip()) {
                pl.process(rawPage.getPage().getResultItems(), null);
            }
            if (!rawPage.getPage()
                    .isAllResultSkip(
                            modelName.toArray(new String[modelName
                                    .size()]))) {
                rawPage.setStored(true);
            }else{
                System.out.println(rawPage.getPage().getResultItems()+"*****************************");
            }

            if (!rawPage.isExtracted() || !rawPage.isStored())
                pageErrorOutPut.returnErrorPage(rawPage,
                        "May Caused By Model Problem! Or Page Error!");
        }
    }

    /**
     * 获取站点site_html_detail表中，id大于lastId的 BatchExtractSize 条记录
     * @param site
     * @param lastId
     * @return
     */
    private List<RawPage> getPages(String site, int lastId) {
        LinkedList<RawPage> pages = new LinkedList<RawPage>();
        pages = pagedao.getDetailPages(site + "_html_detail",lastId,BatchExtractSize);
        return pages;
    }

    /**
     * 获取站点site的最后id
     * @param site
     * @return
     */
    private int getLastId(String site) {
        /** 从records表中，获取最新的抽取记录的id */
        Integer lastRecord = recordDao.getLastRecord(site);
        if(lastRecord == null){//最新抽取记录为空，则新增
            //从对应站点HTML详情表site_html_detail中，获取最新的id
            lastRecord = pagedao.getMinId(site + "_html_detail") - 1;//TODO 为什么是min(ID)
            //最新id插入records表
            recordDao.insertRecord(site,lastRecord);
        }
        return lastRecord;
    }
}

